package it.unibo.refolding.pico.ui;

import it.unibo.refolding.common.CommonThings;
import it.unibo.refolding.pico.ContactPointAndSlopeHelper;
import it.unibo.refolding.pico.FileFormat;
import it.unibo.refolding.pico.Parsed;
import it.unibo.refolding.pico.PicoDataAnalyser.ContactPointAndSlope;
import it.unibo.refolding.pico.PicoShapes;
import it.unibo.refolding.pico.PicoUtil;
import it.unibo.refolding.pico.ZLsbDeflectionLsbPoint;
import it.unibo.refolding.pico.stripchart.AnalysisOptions;
import it.unibo.refolding.pico.stripchart.JpHelper;
import it.unimi.dsi.fastutil.doubles.DoubleLists;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.Parser;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Preconditions;

public class ContactPointAndSlopeMain {

	private static final Logger logger = Logger.getLogger(ContactPointAndSlopeMain.class
			.getSimpleName());

	private static JInternalFrame createFrame(DataHolder h) {
		JInternalFrame internalFrame = new JInternalFrame(h.filename(), true, true, true, true);
		internalFrame.setVisible(true);
		return internalFrame;
	}

	protected void quit() {
		System.exit(0);
	}

	/**
	 * Create the GUI and show it. For thread safety, this method should be
	 * invoked from the event-dispatching thread.
	 * 
	 * @param hs
	 */
	private static void createAndShowGUI(
		List<ContactPointAndSlopeHolder> hs, boolean blockContactPointZ, double baselineFraction, double defScale) {
		JFrame frame = new JFrame();
    frame.setUndecorated(false);
    frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
    int inset = 50;
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setBounds(inset, inset, screenSize.width - inset * 2, screenSize.height - inset * 2);
		JDesktopPane desktop = new JDesktopPane();
		for (int i = 0; i < hs.size(); ++i) {
			ContactPointAndSlopeHolder h = hs.get(i);
			final JInternalFrame internalFrame = createFrame(h);
			internalFrame.setLocation(i, i);
      List<Color> segmentColors = PicoShapes.colorsForPieces(1 + h.junctionPoints().size());
      ZDefDrawer drawer = new ZDefDrawer(h, defScale, segmentColors);
      ContactPointAndSlopeZDefPanel panel = new ContactPointAndSlopeZDefPanel(drawer, defScale, h);
			ContactPointAndSlopeZDefListener listener = new ContactPointAndSlopeZDefListener(
				panel, blockContactPointZ, baselineFraction);
			panel.addMouseListener(listener);
			panel.addMouseMotionListener(listener);
			UiUtil.addKeyShortcutForClosing(internalFrame, panel);
			JScrollPane scrollPane = new JScrollPane(panel);
			scrollPane.getHorizontalScrollBar().setUnitIncrement(16);
			scrollPane.getVerticalScrollBar().setUnitIncrement(16);
			internalFrame.getContentPane().add(scrollPane);
			desktop.add(internalFrame);
			try {
				internalFrame.setMaximum(true);
			} catch (PropertyVetoException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}
		frame.setContentPane(desktop);
		// Make dragging a little faster but perhaps uglier.
		desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
		frame.setVisible(true);
	}

	/**
	 * --wildcardPattern="regex:.*[/\\]2116([01][0-9]|20)_[^/\\]*"
	 * --dataDir=.../refolding_data/refolding_target
	 * --contactPointAndSlopeDir=.../refolding_data/refolding_cps
	 * --blockContactPointZ=true --baselineFraction=0.4
	 */
	public static void main(String[] args) throws ParseException, IllegalArgumentException,
			SecurityException, IllegalAccessException, NoSuchFieldException, IOException {
		Options options = new Options();

		Option dataDirOption = new Option("dataDir", true, "directory with stripchart files");
		dataDirOption.setRequired(true);
		dataDirOption.setType(String.class);
		options.addOption(dataDirOption);

		Option inputPathListOption = PicoUtil.createInputPathListOption();
		options.addOption(inputPathListOption);
		
    Option junctionPointsDirOption = JpHelper.createJunctionPointsOption();
    options.addOption(junctionPointsDirOption);
    
    Option defScaleOption = new Option("defScale", true, "defScale for files");
    defScaleOption.setRequired(true);
    options.addOption(defScaleOption);

		Option blockContactPointZOption = new Option("blockContactPointZ", true, "keep z constant");
		blockContactPointZOption.setRequired(true);
		options.addOption(blockContactPointZOption);

		Option baselineFractionOption = new Option("baselineFraction", true, "");
		baselineFractionOption.setRequired(true);
		options.addOption(baselineFractionOption);

		// http://utilitymill.com/utility/Regex_For_Range
		Option wildcardPatternOption = new Option("wildcardPattern", true,
				"glob:<glob> or regex:<regex> for files");
		wildcardPatternOption.setRequired(true);
		wildcardPatternOption.setType(String.class);
		options.addOption(wildcardPatternOption);
		
    Option fileFormatOption = AnalysisOptions.createFileFormatOption();
    options.addOption(fileFormatOption);

		Option contactPointAndSlopeDirOption = ContactPointAndSlopeHelper
				.createContactPointAndSlopeDirOption();
		options.addOption(contactPointAndSlopeDirOption);

		Parser parser = new GnuParser();
		CommandLine line = parser.parse(options, args);
		String dataDir = line.getOptionValue(dataDirOption.getOpt());
		String inputPathList = line.getOptionValue(inputPathListOption.getOpt());
		Preconditions.checkArgument(
			StringUtils.isEmpty(dataDir) ^ StringUtils.isEmpty(inputPathList),
			"Must specify exactly one of dataDir and inputPathList.");
		String wildcardPattern = line.getOptionValue(wildcardPatternOption.getOpt());
    FileFormat fileFormat = FileFormat.valueOf(line.getOptionValue(
      fileFormatOption.getOpt(), FileFormat.NANOSCOPE.name()).toUpperCase(Locale.ROOT));
		String contactPointAndSlopeDir = line
			.getOptionValue(contactPointAndSlopeDirOption.getOpt());
    final double defScale = Double.parseDouble(line.getOptionValue(defScaleOption.getOpt()));
		final boolean blockContactPointZ = Boolean.parseBoolean(line.getOptionValue(blockContactPointZOption
			.getOpt()));
		final double baselineFraction = Double.parseDouble(
			line.getOptionValue(baselineFractionOption.getOpt()));
		Preconditions.checkArgument(0 <= baselineFraction & baselineFraction <= 1);
    String junctionPointsDirAsString = line.getOptionValue(junctionPointsDirOption.getOpt());
    Path junctionPointsDir = StringUtils.isEmpty(junctionPointsDirAsString) ? null
      : Paths.get(junctionPointsDirAsString);

		Path contactPointAndSlopeDirectory = Paths.get(contactPointAndSlopeDir);
		CommonThings.createDirectoryPathIfNeeded(contactPointAndSlopeDirectory);
		List<Path> files = PicoUtil.computeFileList(dataDir, inputPathList, wildcardPattern);
		
		final List<ContactPointAndSlopeHolder> hs = new ArrayList<>();
		for (Path file : files) {
      Parsed p = PicoUtil.parse(file, fileFormat, null, null, DoubleLists.EMPTY_LIST, junctionPointsDir);
			Path contactPointAndSlopeFile = ContactPointAndSlopeHelper.contactPointAndSlopeFile(
					contactPointAndSlopeDirectory, file.getFileName().toString());
			final ZLsbDeflectionLsbPoint contactPoint;
			final double slope;
			if (Files.isRegularFile(contactPointAndSlopeFile)) {
				ContactPointAndSlope cps = ContactPointAndSlopeHelper
						.extractContactPointAndSlope(contactPointAndSlopeFile);
				contactPoint = cps.contactPoint();
				slope = cps.slopeZLsbDefLsb;
			} else {
				contactPoint = null;
				slope = Double.NaN;
			}
      ContactPointAndSlopeHolder h = new ContactPointAndSlopeHolder(contactPointAndSlopeFile,
					p.z, p.def, contactPoint, slope, p.jp);
			hs.add(h);
		}
		logger.log(Level.INFO, "Curves: {0}", hs.size());
    if (!hs.isEmpty()) {
      javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          createAndShowGUI(hs, blockContactPointZ, baselineFraction, defScale);
        }
      });
    }
	}
}
